﻿using System;
using System.IO;
using System.IO.Ports;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using XBee;
using XBee.Frames;

namespace TDSServerLib{
    // A delegate type for hooking up change notifications.

    public enum CommandTDSMoteType : byte
    {
        USER_NAME = 0x00,
        USER_EXCERSICE_SERIES = 0x01,
        SEND_DATA_EX = 0x02,
        SEND_UID = 0xA0,
        END_SEND_DATA_EX = 0x12,
        CONTINUE = 0xFA,
        CONNECTION = 0xFB,
        END_TRANSMISION = 0xFF
    } 

    public class ReceivedDataTDSServerLib : EventArgs
    {
        public string UID;
        public XBeeAddress64 Addr64;
        public XBeeAddress16 Addr16;
    }

    public class ReceivedExcersiceTDSServerLib : EventArgs
    {
        public byte[] _data;
        public XBeeAddress64 Addr64;
        public XBeeAddress16 Addr16;
        public ExcersiceSerieInformation ExcersiceInformation;
    }

    public delegate void ReceivedDataXbeeLib(object sender, ReceivedDataTDSServerLib e);

    public delegate void ReceivedExcersiceXbeeLib(object sender, ReceivedExcersiceTDSServerLib e);
    public delegate void ReceivedEndExcersiceXbeeLib(object sender, ReceivedExcersiceTDSServerLib e);

    

    public class ServerClass{
        // Declaring a XBee device
        XBee.XBee _xbee = new XBee.XBee() { ApiType = XBee.ApiTypeValue.EnabledWithEscape };

        // Declaring a processing object for data received
        Processing _DataProcess = new Processing();

        public MachinesContainer   _machines;
        XBee.XBeeAddress64 _addr_Maquina_01 = new XBee.XBeeAddress64(0x0013a200409C665A);
        XBee.XBeeAddress64 _addr_Maquina_02 = new XBee.XBeeAddress64(0x0013a20040925999);

        //public int _weight = 0;

        Thread _rxThread;

        public event ReceivedDataXbeeLib ReceivedDataXbee;
        public event ReceivedExcersiceXbeeLib ReceivedExcersiceXbee;
        public event ReceivedEndExcersiceXbeeLib ReceivedEndExcersiceXbee;
        
        // Invoke the Changed event; called whenever list changes
        protected virtual void OnReceivedDataXbee(ReceivedDataTDSServerLib e)
        {
            if (ReceivedDataXbee != null)
                ReceivedDataXbee(this, e);
        }

        // Invoke the Changed event; called whenever list changes
        protected virtual void OnReceivedExcersiceXbee(ReceivedExcersiceTDSServerLib e)
        {
            if (ReceivedExcersiceXbee != null)
                ReceivedExcersiceXbee(this, e);
        }

        // Invoke the Changed event; called whenever list changes
        protected virtual void OnReceivedEndExcersiceXbee(ReceivedExcersiceTDSServerLib e)
        {
            if (ReceivedEndExcersiceXbee != null)
                ReceivedEndExcersiceXbee(this, e);
        }
        

        /**
         *  SERIAL PORT 
         */

        public Object getSerialPortNames ( ){
            var _serialPorts = SerialPort.GetPortNames();
            Object _data = _serialPorts;
            return _data;
        }

        public void connect( String _portNumber){
            _xbee.SetConnection (new XBee.SerialConnection(_portNumber, 115200));
        }

        public void setup(){
            _xbee.ReceivedDataXbee += new ReceivedDataXbeeHandler(NewDataReceivedXbee);
            _rxThread = new Thread(new ThreadStart(check_receive_xbee));
            //_rxThread.Start();
        }

        public void disconnect(){

        }

        public void ReadDatabase(){
            _machines = MachinesContainer.Load("Machines.xml");
            _DataProcess.SetupDataBaseMachine(_machines);
        }
        
        public String getInformationXBee_APIType ( ){
            var request = new ATCommand(AT.ApiEnable) { FrameId = 1 };
            var frame = _xbee.ExecuteQuery(request, 1000);
            var value = ((ATCommandResponse)frame).Value;

            String _APIType = String.Format("API type: {0}", ((ATLongValue)value).Value);
            return _APIType;
        }

        public String getInformationXBee_Baud_Rate ( ){
            var request = new ATCommand(AT.BaudRate) { FrameId = 1 };
            var frame = _xbee.ExecuteQuery(request, 1000);
            var value = ((ATCommandResponse)frame).Value;

            String _Baud_Rate = String.Format("Baud rate: {0}", ((ATLongValue)value).Value);
            return _Baud_Rate;
        }

        public String getInformationXBee_Max_Payload (){
            var request = new ATCommand(AT.MaximumPayloadLenght) { FrameId = 1 };
            var frame = _xbee.ExecuteQuery(request, 1000);
            var value = ((ATCommandResponse)frame).Value;

            String _Max_Payload = String.Format("Maximum Payload is: {0}", ((ATLongValue)value).Value);
            return _Max_Payload;
        }

        public String getInformationXBee_Firmware_Version ( ){
            var request = new ATCommand(AT.FirmwareVersion) { FrameId = 1 };
            var frame = _xbee.ExecuteQuery(request, 1000);
            var value = ((ATCommandResponse)frame).Value;

            String _Max_Payload = String.Format("Firmware Version: {0:X4}", ((ATLongValue)value).Value);
            return _Max_Payload;
        }
        
        
        public String server_test_send( String addr){

            XBee.XBeeAddress64 _addr_test_64 = new XBee.XBeeAddress64(0x0013a20040925999);
            XBee.XBeeAddress16 _addr_test_16 = new XBee.XBeeAddress16(0xFFFE);
            XBee.XBeeNode _node_test = new XBee.XBeeNode();
            _node_test.Address64 = _addr_test_64;
            _node_test.Address16 = _addr_test_16;

            var request = new TransmitDataRequest(_node_test) { FrameId = 1 };
            request.FrameId = 1;
            request.SetRFData(new byte[] { 0xFF, 0x55, 0xFF });
            
            var frame = _xbee.ExecuteQuery(request, 1000);

            if (frame.GetCommandId() == XBeeAPICommandId.TRANSMIT_STATUS_RESPONSE)
            {
            }

            String _Max_Payload = String.Format("Data: {0:X4}", 0x55);
            return _Max_Payload;

               
        }

        public bool send_data_to_xbee(XBeeAddress64 _addr64, byte[] _data, CommandTDSMoteType _cmd){
            XBeeNode _node_tx = new XBeeNode();

            _node_tx.Address64 = _addr64;
            _node_tx.Address16 = new XBeeAddress16(0xFFFE);
            //var request_tx_exp = new ExplicitAddressingTransmit(_node_tx) { FrameId = 1 };
            var request_tx = new TransmitDataRequest(_node_tx) { FrameId = 1 };
            request_tx.FrameId = 1;

            var _tx = new MemoryStream();

            _tx.WriteByte((byte)_cmd);
            _tx.Write(_data, 0, _data.Length);

            request_tx.SetRFData(_tx.ToArray());

            var frame_tx = _xbee.ExecuteQuery(request_tx, 1000);

            //return true;

            
            try{
                if (_xbee.lastFrame.GetType() == typeof(ZigBeeTransmitStatus))
                {
                    var value = ((ZigBeeTransmitStatus)_xbee.lastFrame).DeliveryStatus;

                    if (value == ZigBeeTransmitStatus.DeliveryStatusType.Success)
                        return true;
                    else
                        return false;
                }
                else
                    return false;
            }
            catch{
                return false;
            }
        }

        // This will be called whenever the list changes.
        private void NewDataReceivedXbee(object sender, ReceivedDataEventArgs e){
            ReceivedDataTDSServerLib _arg = new ReceivedDataTDSServerLib();
            ReceivedExcersiceTDSServerLib _arg_ex = new ReceivedExcersiceTDSServerLib();
            switch (((ZigBeeReceivePacket)_xbee.lastFrame).Data[0])
            {
                case ((byte)CommandTDSMoteType.SEND_DATA_EX):
                    var _data = new MemoryStream();
                    if (((ZigBeeReceivePacket)_xbee.lastFrame).Data.Length > 1)
                        _data.Write(((ZigBeeReceivePacket)_xbee.lastFrame).Data, 1, ((ZigBeeReceivePacket)_xbee.lastFrame).Data.Length-1);
                    else
                        _data.Write(((ZigBeeReceivePacket)_xbee.lastFrame).Data, 0, ((ZigBeeReceivePacket)_xbee.lastFrame).Data.Length-1);
                    _arg_ex._data = _data.ToArray();
                    _arg_ex.Addr64 = new XBeeAddress64(0);
                    _arg_ex.Addr64 = ((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64;

                    int _index_case_send_data_ex = 0;
                    foreach (Machine _machine in _machines.Machines){
                        XBeeAddress64 _addr = new XBeeAddress64 (ulong.Parse(_machine.MAC, System.Globalization.NumberStyles.HexNumber));
                        if (((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64.Equals(_addr)){
                            _DataProcess.SaveNewData(_arg_ex._data, _index_case_send_data_ex);
                        }
                        _index_case_send_data_ex++;
                    }

                    while (!send_data_to_xbee(_arg_ex.Addr64, new byte[] { 0x00 }, CommandTDSMoteType.CONTINUE)){}
                    /*
                    if (((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64.Equals(_addr_Maquina_01))
                        _DataProcess.SaveNewData(_arg_ex._data, FitnesExcersice.POLEA_ALTA);
                    else if (((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64.Equals(_addr_Maquina_02))
                        _DataProcess.SaveNewData(_arg_ex._data, FitnesExcersice.CUADRICEPS);
                     * */
                    OnReceivedExcersiceXbee(_arg_ex);
                    
                    break;
                case ((byte)CommandTDSMoteType.END_SEND_DATA_EX):
                    _arg_ex.Addr64 = new XBeeAddress64(0);
                    _arg_ex.Addr64 = ((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64;
                    
                    int _index_case_end_send_data_ex = 0;
                    foreach (Machine _machine in _machines.Machines){
                        XBeeAddress64 _addr = new XBeeAddress64 (ulong.Parse(_machine.MAC, System.Globalization.NumberStyles.HexNumber));
                        if (((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64.Equals(_addr)){
                            _DataProcess.ProcessExcersice(_index_case_end_send_data_ex, ((ZigBeeReceivePacket)_xbee.lastFrame).Data[1]);
                            _arg_ex.ExcersiceInformation = _DataProcess.GetDataExcersice(_index_case_end_send_data_ex);
                            _arg_ex.ExcersiceInformation.weight = ((ZigBeeReceivePacket)_xbee.lastFrame).Data[1];
                            _arg_ex.ExcersiceInformation.time = ((ZigBeeReceivePacket)_xbee.lastFrame).Data[2];
                            _arg_ex.ExcersiceInformation.rep = ((ZigBeeReceivePacket)_xbee.lastFrame).Data[3];
                        }
                        _index_case_end_send_data_ex++;
                    }
                    
                    

                    /*
                    if (((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64.Equals(_addr_Maquina_01)){
                        _DataProcess.ProcessExcersice(FitnesExcersice.POLEA_ALTA, _weight);
                        _arg_ex.ExcersiceInformation = _DataProcess.GetDataExcersice(FitnesExcersice.POLEA_ALTA);
                    }
                    else if (((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64.Equals(_addr_Maquina_02)){
                        _DataProcess.ProcessExcersice(FitnesExcersice.CUADRICEPS, _weight);
                        _arg_ex.ExcersiceInformation = _DataProcess.GetDataExcersice(FitnesExcersice.CUADRICEPS);
                    }
                     * */
                    OnReceivedEndExcersiceXbee(_arg_ex);
                    /*
                    while (!send_data_to_xbee(_arg_ex.Addr64, new byte[] { 0x00 }, CommandTDSMoteType.CONTINUE))
                    {
                    }
                     * */
                    break;
                case ((byte)CommandTDSMoteType.SEND_UID):
                    var _uid = new MemoryStream();
                    _uid.Write(((ZigBeeReceivePacket)_xbee.lastFrame).Data, 1, 4);
                    _arg.UID = BitConverter.ToString(_uid.ToArray());
                    _arg_ex.Addr64 = new XBeeAddress64(0);
                    _arg.Addr64 = ((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64;
                    int _index_case_send_uid = 0;
                    foreach (Machine _machine in _machines.Machines){
                        XBeeAddress64 _addr = new XBeeAddress64 (ulong.Parse(_machine.MAC, System.Globalization.NumberStyles.HexNumber));
                        if (((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64.Equals(_addr)){
                            _DataProcess.ResetData(_index_case_send_uid);
                        }
                        _index_case_send_uid++;
                    }

                    while (!send_data_to_xbee(_arg.Addr64, new byte[] { 0x00 }, CommandTDSMoteType.CONTINUE)) { }

                    /*
                    if (((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64.Equals(_addr_Maquina_01))
                        _DataProcess.ResetData(FitnesExcersice.POLEA_ALTA);
                    else if (((ZigBeeReceivePacket)_xbee.lastFrame).Source.Address64.Equals(_addr_Maquina_02))
                        _DataProcess.ResetData(FitnesExcersice.CUADRICEPS);
                     * */
                    OnReceivedDataXbee(_arg);
                    break;
                default:
                    break;
            }

            
            
            
        }

        public void SetWeight(int weight){
            //_weight = weight;
        }

        public void check_receive_xbee ( ){
            while (true){
                if (_xbee.DataReceived == true){
                    
                    _xbee.DataReceived = false;
                }
            }

        }
         
        
    }
}
